/*!
 * Mcake v1.2.0 (http://apefate.com)
 * Copyright 2016 猿分网
 * author：淡淡的月饼
 * Update time：2016-12-5
 */

;(function(win,undefined){
    //重要变量声明
    var document = window.document,  //document对象
        location = window.location,  //浏览器新窗口
        version = "1.2.0",//当前版本 
        // 空数组对象 用于获得一些数组的方法
        emptyArray = [],
        //查找数组某个值方法
        arr_indexOf = emptyArray.indexOf,
        //获得arr_push方法
        arr_push = emptyArray.push,
        //储存常见类型的 typeof 的哈希表
        // Boolean Number String Function Array Date RegExp Object Error Symbol
        typeClass = {},
        obj_toString = typeClass.toString, //对象类型名称转字符串
        obj_has = typeClass.hasOwnProperty,
        obj_keys = Object.keys; //获得对象key名称 返回数组
        


    //主函数 用于暴露接口
    var Mcake = function(selector,context){
        //内部初始化实例
        return new Mcake.prototype.init();
    }
    //原型方法和属性
    Mcake.fn = Mcake.prototype = {
        constructor: Mcake,
        //构造对象
        init:function(){},
        attr:function(){},
        find:function(){},
        first:function(){},
        last:function(){},
        eq:function(){},
        children:function(){},
        parent:function(){},
        parents:function(){},
        get:function(){}
    };
    
    // 设置初始化构造函数的原型指向 主函数的原型 确保所有通过这个方法生成的实例 this 所指向的 仍然是 Mcake.fn(Mcake.prototype)
    Mcake.fn.init.prototype = Mcake.prototype;

    //////////////////////////
    //------核心函数--------//
    //////////////////////////
    
    /**
     * 核心-合并对象的方法
     * 用法一：Mcake.extend({abc:"abc"})/Mcake.fn.extend({abc:"abc"}) 将名为abc的属性或方法合并到Mcake 或 Mcake实例对象上
     * 用法二：Mcake.extend({},obj1,obj2) 将obj1和obj2合并到第一个新的对象上
     * 用法三：Mcake.extend(true,obj1,obj2)将obj1后面的所有对象 深度拷贝到obj1
     */
    Mcake.extend = Mcake.fn.extend = function() {
        var src, copyIsArray, copy, name, options, clone,
            target = arguments[0] || {},
            i = 1,
            length = arguments.length,
            deep = false;
     
        // target 是传入的第一个参数
        // 如果第一个参数是布尔类型，则表示是否要深递归，
        if (typeof target === "boolean") {
            deep = target;
            target = arguments[1] || {};
            // 如果传了类型为 boolean 的第一个参数，i 则从 2 开始
            i = 2;
        }
     
        // 如果传入的第一个参数是 字符串或者其他
        if (typeof target !== "object" && !Mcake.isFunction(target)) {
            target = {};
        }
    
        // 如果参数的长度为 1 ，表示是 Mcake 静态方法
        if (length === i) {
            target = this;
            --i;
        }
     
        // 可以传入多个复制源 i 是从 1或2 开始的
        for (; i < length; i++) {
            // 将每个源的属性全部复制到 target 上
            if ((options = arguments[i]) != null) {
                for (name in options) {
                    // src 是源（即本身）的值
                    // copy 是即将要复制过去的值
                    src = target[name];
                    copy = options[name];
     
                    // 防止有环，例如 extend(true, target, {'target':target});
                    if (target === copy) {
                        continue;
                    }
     
                    // 这里是递归调用，最终都会到下面的 else if 分支
                    // isJson 用于测试是否为纯粹的对象
                    // 纯粹的对象指的是 通过 "{}" 或者 "new Object" 创建的
                    // 如果是深复制
                    if (deep && copy && (Mcake.isJson(copy) || (copyIsArray = Mcake.isArray(copy)))) {
                        // 数组
                        if (copyIsArray) {
                            copyIsArray = false;
                            clone = src && Mcake.isArray(src) ? src : [];
                            // 对象
                        } else {
                            clone = src && Mcake.isJson(src) ? src : {};
                        }
     
                        // 递归
                        target[name] = Mcake.extend(deep, clone, copy);
     
                        // 最终都会到这条分支
                        // 简单的值覆盖
                    } else if (copy !== undefined) {
                        target[name] = copy;
                    }
                }
            }
        }
     
        // Return the modified object
        // 返回新的 target
        // 如果 i < length ，是直接返回没经过处理的 target，也就是 arguments[0]
        // 也就是如果不传需要覆盖的源，调用 $.extend 其实是增加 Mcake 的静态方法
        return target;
    };


    //////////////////////////
    //------基础函数--------//
    //////////////////////////
    /**
     * 常用的一些基础方法
     */
    Mcake.extend({
        // 遍历 obj 所有元素（数组、对象数组、对象），执行 callback 方法，最终还是返回 obj
        // 注意1：callback.call(obj[i], i, obj[i]) 函数执行的环境和参数
        // 注意2：=== false) return obj 一旦有函数返回 false，即跳出循环，类似 break
        // 注意3：无论哪种情况，最终返回的还是 obj
        each:function(obj, callback){
            var i, key;
            //判断传入的元素是否为数组或数组对象
            if (Mcake.likeArray(obj)) {
                for (i = 0; i < obj.length; i++)
                    if (callback.call(obj[i], i, obj[i]) === false) return obj
            } 
            else {
                for (key in obj)
                    if (callback.call(obj[key], key, obj[key]) === false) return obj
            }
            //无论哪种情况，最终返回的还是 obj
            return obj
        },
        // 错误打印 为 JavaScript 的 "error" 事件绑定一个处理函数
        error: function(msg) {
            throw new Error(msg);
        },
        //对各种设备和浏览器进行检测 返回一个JSON对象 
        //共5个参数：os(操作系统)、ie(IE版本号/false)、weixin(版本号/false)、ios(ios/false)、android(版本号/false)
        device:function(key){
            //获得设备信息并转化成小写
            var agent = navigator.userAgent.toLowerCase();
            //获取版本号
            var getVersion = function(label){
                var exp = new RegExp(label + '/([^\\s\\_\\-]+)');
                label = (agent.match(exp)||[])[1];
                //console.log(agent.match(exp));
                return label || false;
            };
            //返回结果
            var result = {
                //底层操作系统
                os: function(){
                    if(/windows/.test(agent)){
                        return 'windows';
                    } 
                    else if(/linux/.test(agent)){
                        return 'linux';
                    } 
                    else if(/iphone|ipod|ipad|ios/.test(agent)){
                        return 'ios';
                    }
                }(),
                //ie版本
                ie: function(){
                    //由于ie11并没有msie的标识 
                    return (!!win.ActiveXObject || "ActiveXObject" in win) ? ((agent.match(/msie\s(\d+)/) || [])[1] || '11') : false;
                }(),
                //是否为微信
                weixin: getVersion('micromessenger')  
            };

            //任意的key 返回true/false 比如传入
            if(key && !result[key]){
                result[key] = getVersion(key);
            }

            //移动设备
            result.android = /android/.test(agent);
            result.ios = result.os === 'ios';

            return result;
        }
    });
    /**
     * 对各种数据类型进行检测
     */
    //obj_toString.call(obj) = obj.toString();
    Mcake.extend({
        type:function(obj){
            if ( obj == null ) {
                return obj + "";
            }
            return typeof obj === "object" || typeof obj === "function" ?
                typeClass[obj_toString.call(obj)] || "object" :
                typeof obj;
        }, 
        //是否为window对象 window的特点：window.window === window
        isWindow:function(obj){
            return obj != null && obj == obj.window;
        },
        //是否为函数 返回 true/false
        isFunction:function(obj){
            return Mcake.type(obj) === "function";
        },
        //是否为对象 返回 true/false
        isObject:function(obj){
            return Mcake.type(obj) === "object";
        },
        //是否为字符串 返回 true/false
        isString:function(obj){
            return Mcake.type(obj) === "string";
        },
        //是否为数字 返回 true/false
        isNumber:function(obj){
            return Mcake.type(obj) === "number";
        },
        //是否为时间对象 返回 true/false
        isDate:function(obj){
            return Mcake.type(obj) === "date";
        },
        //是否为数组 返回 true/false
        isArray:function(obj){
            return Mcake.type(obj) === "array";
        },
        //是否为DOM对象
        isElement:function(obj){
            return !!(obj && obj.nodeType == 1);
        },
        //是否为纯粹的对象 比如 {} 或 new Object(); 和jquery 的isPlainObject一样 返回 true/false
        isJson:function(obj){
            var key;

            if (!obj || Mcake.type(obj) !== "object" || obj.nodeType || Mcake.isWindow(obj)) {
                return false;
            }
            try {
                if (obj.constructor && !Mcake.has(obj,"constructor") && !Mcake.has(obj.constructor.prototype,"isPrototypeOf")){
                    return false;
                }
            } catch (e) {
                return false;
            }

            for (key in obj) {}

            return key === undefined || Mcake.has(obj,key);
        },
        //是否为数组或数组对象 返回 true/false
        likeArray:function(obj){
            //获得长度和类型
            var length = obj.length,
                type = Mcake.type(obj);
            //如果是window对象直接返回false
            if (Mcake.isWindow(obj)) {
                return false;
            }
            //如果是数组 返回ture
            if (obj.nodeType === 1 && length) {
                return true;
            }
            return type === "array" || type !== "function" &&
                (length === 0 ||
                    typeof length === "number" && length > 0 && (length - 1) in obj);
        },
        //是否为空对象 返回 true/false
        isEmptyObject:function(obj){
            //只要存在一个值就返回false并结束循环
            for (var name in obj) {
                return false;
            }
            return true;
        },
        //判断两个数据是否完全相等 支持所有原始数据类型和对象数据类型的比较
        isEqual:function(a,b){
            return equal(a,b);
        },
        //检查一个属性是否属于对象本身, 而非原型链中 返回 true/false
        has:function(obj,key){
            return obj_has.call(obj,key);
        },
        //检查一个字符串是否为合法的邮箱
        isEmail:function(str){
            //验证是否为合法的邮箱
            var reg_email = /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+/;
            return reg_email.test(str);
        },
        //检查一个字符串是否为合法的手机号码
        isPhoneNumber:function(str){
            //验证是否为合法的手机号码 第一个数字为1 第二个数字为3/4/5/8 11位数的数字
            var reg_phone = /^1[3,4,5,8]\d{9}$/;
            return reg_phone.test(str);
        },
        //检查一个字符串是否为合法的电话号码
        isTelNumber:function(str){
            //验证是否为合法的电话号码 格式：010-8788883
            var reg_tel = /^0[\d]{2,3}-[\d]{7,8}$/;
            return reg_tel.test(str);
        },
        /**
         * 检查一个字符串是否为合法的身份证号码
         * 传入一个字符串或数字
         * 返回一个对象，参数如下：msg(提示信息)、state(验证状态：true/false)、prov(所在省份)、birthday(出生日期：年/月/日)、sex(性别：男/女)、addressCode(地址代码：前六位)、input(当前检验的身份证号码)
         */
        isIdCard:function(card){
            //先将传入的身份证号码去空格并转成大写字符串
            card = Mcake.trim(card).toUpperCase();
            //变量声明
            var ret = {
                    state:false    //验证状态 默认为不通过
                },
                prov,              //省份
                birthday,          //出生日期
                sex,               //性别
                cardArr,           //用于存放处理后的身份证号码
                cardLength = card.length,  //身份证长度
                //身份证省份查询地图
                provMap = {11:"北京",12:"天津",13:"河北",14:"山西",15:"内蒙古",21:"辽宁",22:"吉林",23:"黑龙江",31:"上海",32:"江苏",33:"浙江",34:"安徽",35:"福建",36:"江西",37:"山东",41:"河南",42:"湖北",43:"湖南",44:"广东",45:"广西",46:"海南",50:"重庆",51:"四川",52:"贵州",53:"云南",54:"西藏",61:"陕西",62:"甘肃",63:"青海",64:"宁夏",65:"新疆",71:"台湾",81:"香港",82:"澳门",91:"国外"},
                //性别查询地图
                sexMap = {0:"女",1:"男"},
                // 加权因子 用于计算18位省份证最后一位验证码所在位置
                wiArr = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2],
                // 身份证校验码查询地图
                checkCodeMap = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'],
                //身份证号码验证正则 15位数字或者17位数字+一位校验码
                reg_card = /(^\d{15}$)|(^\d{17}(\d|X)$)/,
                //切分15位和18位身份证的正则 结果反馈给cardArr
                reg_split_15 = /^(\d{6})(\d{2})(\d{2})(\d{2})(\d{3})$/,
                reg_split_18 = /^(\d{6})(\d{4})(\d{2})(\d{2})(\d{3})([0-9]|X)$/;

            //检验身份证号码是否为空
            if(cardLength === 0){
                ret.msg = "身份证号码不能为空！";
            }
            //检验身份证号码位数和格式
            else if(!reg_card.test(card)){
                ret.msg = "身份证号码格式不对！";
            }
            //如果符合要求 进入严格检验
            else{
                //检验所在省份
                if(!checkProvince()){
                    ret.msg = "身份证所在地区非法！";
                }
                //检测身份证出生日期范围
                else if(!checkBrith()){
                    ret.msg = "身份证出生日期错误！";
                }
                //检测18位身份证最后一位验证码 只有18位身份证才需要检测
                else if(cardLength == 18 && !checkParity()){
                    ret.msg = "身份证最后一位校验码错误！";
                }
                //如果所有检验都通过了 返回成功的结果
                else{
                    //返回结果
                    ret = {
                        state:true,              //验证状态
                        msg:"通过验证！",        //提示信息
                        prov:prov,               //省份
                        birthday:birthday,       //出生日期
                        sex:getCardSex(),        //性别
                        addressCode:cardArr[1],  //城市代码
                        input:card               //当前检测身份证号码
                    }
                }
            }

            //检测省份
            function checkProvince(){ 
                var nowProv = provMap[card.substr(0,2)];
                //如果省份存在
                if(nowProv){
                    //储存当前身份证所在省份
                    prov = nowProv;
                    return true; 
                }
                else{
                    return false;
                }
            }
            //检测出生日期
            function checkBrith(){
                //年、月、日
                var year,month,day;
                //身份证15位时，次序为省（3位）市（3位）年（2位）月（2位）日（2位）校验位（3位），皆为数字  
                if(cardLength == 15){    
                    //切分身份证号码
                    cardArr = card.match(reg_split_15);  
                    //设置15位身份证号码的年份 因为1999年之前才是15位
                    year = '19'+cardArr[2];   
                }  
                //身份证18位时，次序为省（3位）市（3位）年（4位）月（2位）日（2位）校验位（4位），校验位末尾可能为X 
                else{
                    //切分身份证号码
                    cardArr = card.match(reg_split_18);  
                    //设置年份
                    year = cardArr[2]; 
                }
                //获得月、日、生日时间对象
                month = cardArr[3];  
                day = cardArr[4];  
                birthday = year+'/'+month+'/'+day

                //检验出生日期是否合法
                return verifyBrith(year,month,day,birthday);
            }
            //检验出生日期是否合法
            function verifyBrith(year,month,day,birthday){
                var now = new Date(), //当前时间
                    newBirth = new Date(birthday),//获得出生日期的时间对象
                    result = false;
                //年月日是否合理  
                if(newBirth.getFullYear() == year && (newBirth.getMonth() + 1) == month && newBirth.getDate() == day){  
                    //判断年份的范围（3岁到100岁之间)  
                    var time = now.getFullYear() - year;  
                    if(time >= 3 && time <= 100){  
                        result =  true;  
                    }    
                }  
                return result;
            }
            //获得当然身份证性别
            function getCardSex(){
                return sexMap[cardArr[5] % 2];
            }
            //检测18位身份证后面的验证码是否正确
            function checkParity(){
                // 声明加权求和变量
                var sum = 0,i,result = false;
                //计算权求和
                for (i = 0; i < 17; i++) {  
                    sum += wiArr[i] * card.substr(i, 1);// 加权求和    
                }  
                //判断验证码是否正确
                if (card.substr(17, 1) == checkCodeMap[sum % 11]){  
                    result =  true;  
                } 
                return result;
            }
            //返回结果 
            return ret;
        }
    });
    /* 数据类型检测的底层支持 */
    //设置各种数据类型的查找名称到typeClass中 用于判断类型是查找
    Mcake.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "), function(i, name) {
        typeClass[ "[object "+name+"]" ] = name.toLowerCase();
    });

    //判断两个数据是否相等
    function equal(a,b,aStack,bStack){
        // 检查两个简单数据类型的值是否相等
        // 对于复合数据类型, 如果它们来自同一个引用, 则认为其相等
        // 如果被比较的值其中包含0, 则检查另一个值是否为-0, 因为 0 === -0 是成立的
        // 而 1 / 0 == 1 / -0 是不成立的(1 / 0值为Infinity, 1 / -0值为-Infinity, 而Infinity不等于-Infinity)
        if (a === b) return a !== 0 || 1 / a === 1 / b;
        //如果两个值中有一个为空 则返回false
        if (a == null || b == null) return false;
        //如果为NaN 和自身是不相等的
        if (a !== a) return b !== b;
        //如果a和b都不是对象 返回false
        var type = typeof a;
        if (type !== 'function' && type !== 'object' && typeof b != 'object') return false;
        //对 对象类型的数据进行深入的比较
        return deepEqual(a, b, aStack, bStack);
    }
    //深度比较两个数据 主要是对数组和对象的比较
    function deepEqual(a, b, aStack, bStack) {
        //下面则是原始数据类型判断的最后一步，根据[[Class]]值进行判断，这种判断能覆盖原始数据类型未被覆盖到的所有剩余情况，首先取得a的[[Class]]并和b的[[Class]]先比较一下：
        var className = Mcake.type(a);
        if (className !== Mcake.type(b)) return false;
        //然后通过一个switch (className)语句进行分类判断
        switch (className) {
            //字符串和正则 它们的判断方式一样，统一转换为字符串后进行严格相等的判断：
            case 'regexp':
            case 'string':
                return '' + a === '' + b;
            //数字类型 先考虑特殊情况NaN，a和b都是NaN时它们会不等，但应该把它们看做相等的，因为NaN总是表现出一样的性质，解决办法是判断a和b是否分别为NaN。最后再判断一次相等性，同时剔除-0的情况，这和eq方法刚开始的逻辑似乎重复了，不知道是不是：
            case 'number':
                if (+a !== +a) return +b !== +b;
                return +a === 0 ? 1 / +a === 1 / b : +a === +b;
            //时间和布尔值 直接调用===
            case 'date':
            case 'boolean':
                return +a === +b;
            //symbol类型 新增的基本数据类型
            case 'symbol':
                return SymbolProto.valueOf.call(a) === SymbolProto.valueOf.call(b);
        }
        //判断是否为数组
        var areArrays = className === 'array';
        //如果不是数组
        if (!areArrays) {
            //先确定是否为对象 如果有一个不是 直接返回false
            if (typeof a != 'object' || typeof b != 'object') return false;
            //先要排除一种情况，那就是如果是对象的话，可以先比较它们的构造函数，构造函数不同的话，即使对象内的值相同，两个对象也是不同的
            var aCtor = a.constructor, bCtor = b.constructor;
            if (aCtor !== bCtor && !(Mcake.isFunction(aCtor) && aCtor instanceof aCtor &&
            Mcake.isFunction(bCtor) && bCtor instanceof bCtor) && ('constructor' in a && 'constructor' in b)) {
                return false;
            }
        }
        //通过构造函数的比较后，即进入具体包含值的比较，后面紧跟的while循环在第一次遍历的时候是不会执行的。后面将a和b分别压入堆栈，堆栈的作用是按照顺序存放比较对象的元素值，并递归调用eq方法自身。对于a或者b来说，如果某个子元素仍然是对象或者数组，则会将这个子元素继续拆分，直到全部拆分为eq方法前半部分所写的，可以比较的“基本单元”为止，一旦有任何一个元素不相等，便会触发一连串的return false。至于数组和对象的区别并不是太重要
        aStack = aStack || [];
        bStack = bStack || [];
        var length = aStack.length;
        while (length--) {
            if (aStack[length] === a) return bStack[length] === b;
        }

        // 将第一个对象添加到遍历的对象的堆栈中。
        aStack.push(a);
        bStack.push(b);

        // 递归比较对象和数组
        if (areArrays) {
            // 比较数组的长度，以确定是否有必要进行比较深入的比较。
            length = a.length;
            if (length !== b.length) return false;
            // 深入比较的内容，忽略非数字属性。
            while (length--) {
                if(!equal(a[length], b[length], aStack, bStack)) return false;
            }
        } 
        else {
            var keys = Mcake.keys(a), key;
            length = keys.length;
            //确保两个对象在比较深度相等之前都包含相同数量的属性。
            if (Mcake.keys(b).length !== length) return false;
            while (length--) {
                key = keys[length];
                if (!(Mcake.has(b, key) && equal(a[key], b[key], aStack, bStack))) return false;
            }
        }
        aStack.pop();
        bStack.pop();
        return true;
    };
    /**
     * 对于各种类型数据的基本操作
     */
    Mcake.extend({
        //清除字符串空格 
        trim:function(str){
            //判断是否为空 如果为空返回空字符  如果不为空 先强制转换成字符串 然后去除空格
            //这里为了提高性能 使用了两次正则 分别去除前面和后面空格
            //更多方法可参考http://www.cnblogs.com/rubylouvre/archive/2009/09/18/1568794.html
            return str == null ? "" : (str + "").replace(/^\s\s*/, '').replace(/\s\s*$/, '');
        },
        //判断某个值是否在数组中  
        //第一个参数：查找内容(必填)
        //第二个参数：查找目标数组(必填)
        //第三个参数：开始查找索引(选填)
        //成功返回查找内容所在索引 没有找到返回-1
        inArray:function(elem, arr, i){
            //如果查找内容和要查找的目标数组都存在
            if(elem && arr){
                //如果浏览器支持原生的数组查找方法
                if(arr_indexOf){
                    return arr_indexOf.call(arr, elem, i);
                }
                //获得数组的长度
                var len = arr.length;
                //开始查找的索引值 默认为从0开始 如果小于0则从总数加指定负数开始查找
                i = i ? i < 0 ? Math.max(0, len + i) : i : 0;

                for (; i < len; i++) {
                    // 这里的(i in arr)判断是为了跳过稀疏数组中的元素
                    // 例如 var arr = []; arr[1] = 1;
                    // 此时 arr == [undefined, 1]
                    // 结果是 => (0 in arr == false) (1 in arr == true)
                    // 测试了一下 $.inArray(undefined, arr, 0)是返回 -1 的
                    if (i in arr && arr[i] === elem) {
                        return i;
                    }
                }
            }
            //如果没有传入参数或数组返回-1
            return -1;
        },
        //去除数组中的null undefined 传入的参数只能为数组
        compactArray:function(arr){
            var ret = [];
            //如果传入的参数是数组
            Mcake.each(arr,function(i,item){
                if(item)
                    ret.push(item);
            });
            return ret;
        },
        //去除数组中重复项
        //第一个参数传入要处理的数组  
        //第二个参数为布尔值(可选)：当等于true是 会对去重后的数组进行排序 
        uniqueArray:function(arr,isSort){
            //创建res数组和json对象
            var res = [],json = {};
            //循环查询
            Mcake.each(arr,function(i,item){
                //判断空json对象中是否存在当前这一项
                if(!json[item]){
                    res.push(item);
                    json[item] = 1;
                }
            });
            //如果传入的值为true 对数组进行排序
            if(isSort == true)
                res.sort(function(a,b){return a-b})
            //返回结果
            return res; 
        },
        //去除JSON对象中值为null或undefined 的项目
        compactJson:function(obj){
            var ret = {};
            //如果传入的参数是数组
            Mcake.each(obj,function(i,item){
                if(item)
                    ret[i]=item;
            });
            return ret;
        },
        //获得JSON对象、数组、数组对象(jQuery对象)的长度
        size:function(obj){
            //如果传入的值为空 返回0
            if (obj == null) return 0;
            //判断是否为数组/数组对象 如果是直接返回length 否则调用keys方法获得长度
            return Mcake.likeArray(obj) ? obj.length : Mcake.keys(obj).length;
        },
        //将XML字符串转化成XML对象
        parseXML:function(xmlStr) {  
            var xmlDoc;  
            // 判断是否为字符串
            if (Mcake.isString(xmlStr)) {  
                // chrome...    
                if (window.DOMParser) {  
                    var tmp = new DOMParser();  
                    xmlDoc = tmp.parseFromString(xmlStr, "text/xml");  
                } 
                // IE浏览器
                else if (window.ActiveXObject) {  
                    xmlDoc = new ActiveXObject("Microsoft.XMLDOM");  
                    xmlDoc.async = false;  
                    xmlDoc.loadXML(xmlStr);  
                }  
            }  
            else {  
                xmlDoc = xmlStr;  
            }  
            return xmlDoc;  
        },
        //将HTML一些特殊字符进行转义
        escapeHTML:function(str){
            if (Mcake.isString(str)) {
                return str
                    .replace(/&/g, '&amp;')
                    .replace(/</g, '&lt;')
                    .replace(/>/g, '&gt;')
                    .replace(/"/g, '&quot;')
                    .replace(/'/g, '&#039;')
                    .replace(/`/g, '&#x60;');
            }
            return str;
        },
        //将时间字符串转换成Date对象
        //使用方式1: Mcake.parseDate("2016-11-30 12:27:55")
        //使用方式2: Mcake.parseDate("2016/11/30 12:27:55")
        //使用方式3: Mcake.parseDate("2016-11-30")
        parseDate:function(str){
            //如果传入的参数为字符串
            return Mcake.isString(str) ? new Date(Date.parse(str.replace(/-/g,"/"))) : null;
        },
        //获得当前时间 或传入一个时间对象 返回一个格式为：年-月-日 时:分:秒 的字符串
        getDate:function(dateObj){
            //判断是否传入了参数 如果存入了参数且为时间对象 则用传入的参数 否则创建一个新的
            dateObj = Mcake.isDate(dateObj) ? dateObj : new Date();
            //将一个小于10的数字前面加0  比如8 会转换成 08
            function plusZero(n){
                if (n < 10)
                    return "0" + n;
                else 
                    return "" + n;
            }
            //返回结果
            return dateObj.getFullYear() + "-" + (dateObj.getMonth()+1) + "-" + dateObj.getDate()+"  "+plusZero(dateObj.getHours())+':'+plusZero(dateObj.getMinutes())+':'+plusZero(dateObj.getSeconds());
        },
        // 本地储存 对HTML5中localStorage对象的封装 支持一次对多个字段进行增删改的操作
        // 使用场景1:创建一张表和数据
        // Mcake.localData("test_table",{key:"字段内容"});
        // 使用场景2:修改表字段数据
        // Mcake.localData("test_table",{key:"我是修改后的内容"});
        // 使用场景3:删除表字段数据 注意这里是null 不是 "null"字符串
        // Mcake.localData("test_table",{key:null});
        // 使用场景4：删除整张表
        // Mcake.localData("test_table",null);
        // 使用场景5:获得整张表的数据 如果不存在会返回一个空对象
        // var data = Mcake.localData("test_table");
        localData:function(table, settings){
            //如果没有传入表名 则创建名称为Mcake的表
            table = table || 'Mcake';
            //如果settings为null，则删除对应的表
            if(settings === null){
              return delete localStorage[table];
            }
            //判断是否已经存在表 没有就创建个新的
            try{
              var data = JSON.parse(localStorage[table]);
            } catch(e){
              var data = {};
            }
            //如果传入的参数为JSON 增删改操作
            if(Mcake.isJson(settings)){
                //合并所有字段
                Mcake.extend(data,settings);
                //去除内容为null和undefined的字段
                data = Mcake.compactJson(data);
            }

            //储存最终数据
            localStorage[table] = JSON.stringify(data);
            //如果没有传参数 最终返回表
            return Mcake.isString(settings) ? data[settings] : data;
        },
        // 对url参数的相关操作
        // 使用场景1：获得当前URL中所有的参数 返回一个JSON对象(有两个特殊参数:href、context)
        // var myUrl = Mcake.url();
        // 使用场景2：获得某一个参数的内容 如果没找到 返回空字符
        // var param = Mcake.url("参数名");
        // 使用场景3：修改、删除、添加某个参数的值 传入JSON {key:value} 当value=null时表示删除
        // var eidtUrl = Mcake.url({key:value,key:null})   返回字符串
        // 使用场景4：抛弃所有的参数 重新创建一个URL路径  在JSON后面再添加一个参数为true
        // var newUrl = Mcake.url({key:value},true)  返回字符串
        url:function(settings,isCreate){
            var result = null;
            //获得URL返回的JSON
            var urlJson = getUrl();
            //判断是否有参数
            if(settings){
                //如果传入的是字符串 - 获取指定名称的参数
                if(Mcake.isString(settings)){
                    //直接返回该参数的值
                    result = urlJson[settings] || "";
                }
                //如果是要更改或添加参数 {name:value}
                else if(Mcake.isJson(settings)){
                    result = setUrl(urlJson,settings,isCreate);
                }
            }
            else{
                //设置返回结果
                result = urlJson;
            }
            return result;
        },
        //获得对象上所有的名称 传入一个对象 
        //返回一个数组 包含对象自身所有的属性 原型链上继承的属性则会被忽略
        keys:function(obj){
            //如果传入的参数不是对象或长度为0 返回空数组
            if(!Mcake.isObject(obj)) return [];
            //如果宿主支持原生的方法 直接调用
            if(obj_keys) return obj_keys(obj);
            //如果原生的方法不存在
            var keys = [];
            for (var key in obj){
                //只返回属于这个对象本身的key 从原型继承的属性不会拷贝
                if(Mcake.has(obj, key)) keys.push(key);
            }
            return keys;
        },
        //获得一个对象的所有值 返回一个数组
        values:function(obj){
            var keys = Mcake.keys(obj), //获得对象所有的key 返回数组
                length = keys.length,  //获得keys数组长度
                values = Array(length);//创建一个新的数组
            //循环所有的key 获得对应的value值
            for (var i = 0; i < length; i++){
                values[i] = obj[keys[i]];
            }
            return values;
        }
    });
    /**
     * 对于URL的相关操作
     */
    //获得当前URL地址上所有的参数
    function getUrl(){
        //返回结果/参数前的字段
        var ret = {};
        //获得URL全部内容
        var context = window.location.href,
            urlArr = context.split("?");
        //判读url中"?"符后的参数是否存在
        if(urlArr.length === 2){
            //将所有参数分割成数组
            var paramArr = urlArr[1].split("&"); 
            //获得所有参数的键/值
            for(var i = 0; i < paramArr.length; i ++) { 
                //获得每一个参数
                var param = paramArr[i].split("=");
                //这里不用unescape() 因为中文参数会乱码
                ret[param[0]]=decodeURI(param[1]); 
            } 
            //返回?前的路径
            ret.href = urlArr[0];
        }
        else{
            //返回全部URL
            ret.href = context;
        }
        ret.context = context;
        
        return ret;
    }
    //设置URL参数
    function setUrl(oldUrlJson,paramJson,isCreate){
        //新的结果
        var ret = {};
        //如果传入的JSON为空 直接返回原链接
        if(Mcake.getJsonLength(paramJson) === 0){
            ret = oldUrlJson;
        }
        else{
            //如果为新建一个url路径
            if(isCreate == true){
                //生成新的URL JSON
                ret.href = oldUrlJson.href;
                //合并所有的参数到结果中
                Mcake.extend(ret,paramJson);
            }
            else{
                //查找是否存在
                Mcake.extend(ret,oldUrlJson,paramJson);
            }
        }
        return urlToStr(ret);
    }
    //将url JSON对象转换成字符串
    function urlToStr(urlJson){
        var param = [];
        for(var i in urlJson){
            //获得所有不为空的参数
            if(i != "href" && i != "context" && urlJson[i]){
                param.push(i+"="+urlJson[i]);
            }
        }
        return urlJson.href+"?"+param.join("&");
    }

    //////////////////////////
    //------事件处理--------//
    //////////////////////////

    Mcake.extend({
        //阻止事件冒泡
        stopEvent:function(e){
            //stopPropagation针对W3C标准主流浏览器 cancelBubble针对IE
            e = e || win.event;
            e.stopPropagation ? e.stopPropagation() : e.cancelBubble = true;
        }

    });

    //////////////////////////
    //------文件处理--------//
    //////////////////////////

    Mcake.extend({
        //下载文件
        download:function(){

        }
    });


    //暴露接口
    win.Mcake = win.M = Mcake;
})(window);

/**
 * 功能组件
 * McakeDropdown   下拉菜单
 * McakeBar        进度条
 * 
 */

/**
 * McakeDropdown 下拉菜单
 */
;(function($,win,document,undefined){
    'use strict';

    // 默认声明 组件名称
    var pluginName = "McakeDropdown";
    // 下拉菜单默认data属性名
    var menuClass = ".button-dropdown";

    // 下拉菜单构造函数
    var McakeDropdown = function(element){
        //设置对象参数
        // this.defaults = defaults;
        this._name = pluginName; //当前组件名称
        this.menuClass = menuClass;  //当前下拉菜单data-progress名称

        //获得当前控制对象
        this.$element = $(element);
        //初始化组件
        this.init();
    }

    //API调用接口
    McakeDropdown.prototype = {
        constructor:McakeDropdown,
        //下拉菜单模块初始化
        init:function(){
            this.toggle();
        },
        //切换状态
        toggle: function() {
            //判断当前是否打开
            if(this.$element.data('dropdown') === 'show') {
             this.hideMenu();
            }
            else {
             this.showMenu();
            }
        },
        //打开菜单
        showMenu: function() {
            this.$element.data('dropdown', 'show');
            this.$element.find('ul').show();
            this.$element.find('.button:first').addClass('active');
        },
        //关闭菜单
        hideMenu: function() {
            this.$element.data('dropdown', 'hide');
            this.$element.find('ul').hide();
            this.$element.find('.button:first').removeClass('active');
        }
    }

    //在构造函数中的一个非常轻量级的插件包装器，
    //防止多个实例
    $.fn[pluginName] = function(){
        return this.each(function(){
            //打印当前实例
            //console.log($.data(this, "plugin_" + pluginName));
            //判断是否已经存在当前实例 如果存在则进行切换状态 否则创建一个新的实例
            if ($.data(this, "plugin_" + pluginName)) {
                $.data(this, "plugin_" + pluginName).toggle();
            }
            else {
                $.data(this, "plugin_" + pluginName, new McakeDropdown(this));
            }
        });
    }

    //按钮其他地方点击关闭
    $(document).on("click",function(e){
        $.each($('[data-buttons=dropdown]'), function(i, value) {
            if ($(e.target.offsetParent)[0] != $(this)[0]) {
                if ($.data(this, "plugin_" + pluginName)) {
                    //关闭所有已经存在的实例
                    $.data(this, "plugin_" + pluginName).hideMenu();
                    $(this).find('ul').hide();
                }
            }
        });
    });


    //绑定下拉菜单按钮点击事件
    $(document).on('click', '[data-buttons=dropdown]', function(e) {
        var $dropdown = $(e.currentTarget);
        $dropdown.McakeDropdown();
    });
    // 阻止a标签按钮的默认事件
    $(document).on('click', '[data-buttons=dropdown] > a', function(e) {
        e.preventDefault();
    });

})(jQuery,window,document);


/**
 * McakeBar 进度条
 */
;(function($,win,document,undefined){
    'use strict';

    // 默认声明 组件名称
    var pluginName = "mcakeProgressBar";
    // 进度条默认data属性名
    var dataProgress = "progressbar";
    // 进度条默认参数
    var defaults = {
        propertyName: "value",
        minWidth:0,       //最小宽度
        maxWidth:100,     //最大宽度
        nowWidth:0,       //当前宽度
        speed:600        //动画速度
    }
    // 进度条构造函数
    var McakeBar = function(element){
        //设置对象参数
        // this.options = $.extend({}, defaults, options); //合并传递的参数和默认参数
        this.defaults = defaults;
        this._name = pluginName; //当前组件名称
        this.dataProgress = dataProgress;  //当前进度条data-progress名称

        //获得当前控制对象
        this.$element = $(element);
        //初始化组件
        this.init();
    }

    //API调用接口
    McakeBar.prototype = {
        constructor:McakeBar,
        //进度条模块初始化
        init:function(){
            //相关参数
            var self = this, //获取当前实例对象
                _defaults = self.defaults,//获取配置项
                $element = self.$element;//获得初始化对象

            //获取进度条最小宽度、最大宽度和当前宽度设置  
            var minWidth = $element.attr("bar-valuemin") || _defaults.minWidth,
                maxWidth = $element.attr("bar-valuemax") || _defaults.maxWidth,
                nowWidth = $element.attr("bar-valuenow") || _defaults.nowWidth,
                speed = $element.attr("bar-speed") || _defaults.speed;

            //进行运动控制 调用核心代码库中的运动算法
            if(win.McakeCore){
                // 启动运动算法
                McakeCore.Algorithm.startMove({
                    startVal:minWidth,                // 运动开始数值
                    endVal:nowWidth,          // 运动结束数值
                    speed:speed,                  // 设置完成时间 当前设定为1秒
                    refreshInterval:9,          // 刷新间隔     当前设定为0.2秒
                    moveCurve:"linear",          // 运动曲线
                    updateBack:function(nowTimes,nowDistance){ //每次运动时回调函数
                        // 得到当前运动位置
                        var value = nowDistance+"%";
                        // 设置运动位置
                        $element.css("width",value).text(value);

                        //打印检测运动状态
                        // console.log("当前为第"+nowTimes+"次运动，运动到了：'"+value+"'的位置");
                    }
                });
            }
            else{
                console.log("没有找到核心代码模块:McakeCore");
            }

            //打印当前实例
            console.log(self);
        }
    }

    //在构造函数中的一个非常轻量级的插件包装器，
    //防止多个实例
    $.fn[pluginName] = function(){
        return this.each(function(){
            new McakeBar(this);
        });
    }

    //初始化进度条动画
    $(function(){
        var $bar = $("[data-progress='progressbar']");
        // console.log($bar);
        $bar.mcakeProgressBar();
    })
 
})(jQuery,window,document);

/*!
 * McakeSearch(搜索提示)
 * Edition: v1.0.0
 * Update time：2016.11.11
 */

;(function($,win,document,undefined){
    'use strict';

    // 默认声明 组件名称
    var pluginName = "McakeSearch";
    // 搜索提示默认class
    var menuClass = ".m-search";

    // 搜索提示构造函数
    var McakeSearch = function(element){

        //获得当前控制对象
        this.$element = $(element);
        //获得输入框
        this.$searchInput = this.$element.find(".m-search-input");
        //获得搜索数据列表UL
        this.$searchList = this.$element.find(".m-search-list");

        //判断是否为下拉菜单类型
        var isSelect = this.$element.attr("mcake-search");
        if(isSelect && isSelect == "select")
            this.isSelect = true;

        //设置对象参数
        this.menuClass = menuClass;  //当前搜索提示data-progress名称
        this._name = pluginName; //当前组件名称

        //初始化组件
        this.init();
    }

    //API调用接口
    McakeSearch.prototype = {
        constructor:McakeSearch,
        //搜索提示模块初始化
        init:function(){
            //切换搜索提示打开状态
            this.toggle();
            //绑定事件
            this.bind();
        },
        //绑定事件
        bind:function(){
            //搜索输入框输入事件
            this.bindSearchInputEvent();
            //绑定搜索数据列表点击事件
            this.bindSearchItemClickEvent();
        },
        //绑定搜索输入框输入事件
        bindSearchInputEvent:function(){
            //获得当前输入框
            var self = this,
                $element = self.$element,
                $searchInput = self.$searchInput;

            $searchInput.unbind('input propertychange');
            //绑定搜索框文字输入事件
            $searchInput.bind('input propertychange',function(){
                //设置下拉菜单打开
                $element.addClass("open");
                //获取输入的值
                var keywords = $(this).val();
                //匹配对应的数据列表
                self.search(keywords);
            });

        },
        //数据列表点击事件
        bindSearchItemClickEvent:function(){
            var self = this,
                isSelect = self.isSelect,//是否模拟为下拉选择框
                $searchInput = self.$searchInput,//输入框
                $searchList = self.$searchList;//数据列表UL
            $searchList.undelegate("click.search");
            //委托点击事件
            $searchList.delegate("li","click.search", function(){
                //获得当前选项内容
                var $this = $(this),
                    thisValue = $this.text();
                //设置到输入框
                $searchInput.val(thisValue);
                //选择完后重新设置搜索值为空
                self.search("");
                //如果模拟为下拉选择框
                if(isSelect){
                    //设置当前项目选中
                    $this.addClass("active").siblings("li").removeClass("active");
                }
            });
        },
        //搜索关键词
        search:function(key){
            var $searchList = this.$searchList,//数据列表UL
                $searchItem = $searchList.find("li"),//获得当前所有列表
                keywords = $.trim(key).toLowerCase(),//取出前后空格 转换小写
                reEscape = new RegExp('(\\' + ['/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\'].join('|\\') + ')', 'g'),//检索关键字
                pattern = '(' + key.replace(reEscape, '\\$1') + ')';//当前关键字变量$1

            //删除之前的高亮
            $searchItem.removeClass("active").find('.m-keywords').contents().unwrap();

            //如果字段不为空
            if(keywords){
              $searchItem
                  .hide() //隐藏所有项目
                  //筛选关键词
                  .filter(function(){
                    //获得当前li
                    var $this = $(this),
                        text = $.trim($this.text()).toLowerCase();
                    
                    /* 查找关键词是否存在 */
                    if(text.toString().indexOf(keywords) != -1){
                        //设置搜索到的列表HTML
                        $this.html(function(i, oldhtml){
                          //将关键词替换成高亮状态 返回最终的文本
                          return oldhtml.replace(new RegExp(pattern, 'gi'), '<span class="m-keywords">$1</span>');
                        })  
                        //让搜索到的列表显示
                        return true;
                    }
                  })
                  //符合条件的项目显示
                  .show();
            }
            else{
                //如果为空 显示全部
                $searchItem.show();
            }

        },
        //切换状态
        toggle: function() {
          //判断当前是否打开
          if(this.$element.data('search') === 'show') {
            this.hideMenu();
          }
          else {
            this.showMenu();
          }
        },
        //打开菜单
        showMenu: function() {
          this.$element.data('search', 'show');
          this.$element.addClass('open');
        },
        //关闭菜单
        hideMenu: function() {
          this.$element.data('search', 'hide');
          this.$element.removeClass('open');
        }
    }

    //在构造函数中的一个非常轻量级的插件包装器，
    $.fn[pluginName] = function(){
        return this.each(function(){
            //打印当前实例
            //console.log($.data(this, "plugin_" + pluginName));
            //判断是否已经存在当前实例 如果存在则进行切换状态 否则创建一个新的实例
            if ($.data(this, "plugin_" + pluginName)) {
              $.data(this, "plugin_" + pluginName).toggle();
            }
            else {
              $.data(this, "plugin_" + pluginName, new McakeSearch(this));
            }
        });
    }

    //按钮其他地方点击关闭
    $(document).on("click",function(e){
        $.each($('[mcake-init=search]'), function(i, value) {
            if ($(e.target.offsetParent)[0] != $(this)[0]) {
                if ($.data(this, "plugin_" + pluginName)) {
                    //关闭所有已经存在的实例
                    $.data(this, "plugin_" + pluginName).hideMenu();
                    $(this).removeClass('open');
                }
            }
        });
    });


    //绑定搜索框点击事件
    $(document).on('click', '[mcake-init=search] input', function(e) {
        var $dropdown = $(e.currentTarget).parent();
        $dropdown.McakeSearch();
    });
})(jQuery,window,document);